package com.saturday.utils;

import cn.hutool.core.util.ObjectUtil;

import java.util.*;

/**
 * @类名称 校验器
 * @类描述 <pre>参数基础校验</pre>
 */
public class ValidatorUtil {

    /**
     * 必填校验
     */
    public static final String REQUIRED = "required";
    /**
     * 整形数字校验
     */
    public static final String INTEGER = "integer";
    /**
     * 包含小数点的数字校验
     */
    public static final String NUMBER = "number";
    /**
     * 整形或小数点数字校验
     */
    public static final String DECIMALS = "decimals";
    /**
     * 手机号码校验
     */
    public static final String MOBILE = "mobile";
    /**
     * 电话号码校验
     */
    public static final String TEL = "tel";
    /**
     * 联系电话校验
     */
    public static final String PHONE = "phone";
    /**
     * 电子邮箱校验
     */
    public static final String EMAIL = "email";
    /**
     * 合法字符校验
     */
    public static final String RIGHTFUL = "rightful";
    /**
     * 日期格式校验
     */
    public static final String DATE = "date";

    /**
     * 日期格式校验
     */
    public static final String IPV4 = "IPV4";
    /**
     * 检验结果
     */
    private boolean result = false;

    /**
     * @return 校验通过返回true, 失败false
     * @方法名称 validation 校验
     * @功能描述 <pre>规则校验</pre>
     * @作者
     */
    public boolean validation() {
        if (null == dataMap) {
            return result;
        }

        if (null == ruleMap) {
            return result;
        }
        result = this.handler();
        return result;
    }

    /**
     * @return
     * @方法名称 buildErrorDescription
     * @功能描述 <pre>构建错误描述信息</pre>
     * @作者
     */
    public Map<String, List<String>> buildErrorDescription() {
        if (descriptionMap == null || errorMap.isEmpty()) {
            return errorMap;
        }

        Set<String> keys = errorMap.keySet();

        Map<String, List<String>> errDescription = new HashMap<String, List<String>>();

        List<String> descriptions = null;
        for (String key : keys) {
            descriptions = new ArrayList<String>();
            List<String> errors = errorMap.get(key);
            String description = descriptionMap.get(key);
            for (String error : errors) {
                descriptions.add(description + error);
            }

            errDescription.put(key, descriptions);
        }

        return errDescription;
    }

    /**
     * @return
     * @方法名称 handler
     * @功能描述 <pre>校验核心处理</pre>
     * @作者
     */
    private boolean handler() {
        this.required(ruleMap.get(REQUIRED));
        this.integer(ruleMap.get(INTEGER));
        this.number(ruleMap.get(NUMBER));
        this.decimals(ruleMap.get(DECIMALS));
        this.email(ruleMap.get(EMAIL));
        this.mobile(ruleMap.get(MOBILE));
        this.tel(ruleMap.get(TEL));
        this.phone(ruleMap.get(PHONE));
        this.rightful(ruleMap.get(RIGHTFUL));
        ;
        this.date(ruleMap.get(DATE));
        this.ipv4(ruleMap.get(IPV4));
        return errorMap.isEmpty();
    }


    private void ipv4(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (RegexUtil.isNull(value)) {
                this.addError(key, "不是正确的ip");
            }
            if (!RegexUtil.isIpv4(value)) {
                this.addError(key, "不是正确的ip");
            }
        }
    }

    /**
     * @param keys 参数名
     * @方法名称 required
     * @功能描述 <pre>必填校验</pre>
     * @作者
     */
    private void required(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (RegexUtil.isNull(value)) {
                this.addError(key, "不能为空");
            }
        }
    }

    /**
     * @param keys 参数名
     * @方法名称 integer
     * @功能描述 <pre>整形数字校验</pre>
     * @作者
     */
    private void integer(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isInteger(value)) {
                this.addError(key, "不是正确的整形数字");
            }
        }
    }

    /**
     * @param keys
     * @方法名称 mobile
     * @功能描述 <pre>手机号码验证</pre>
     * @作者
     */
    private void mobile(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isMobile(value)) {
                this.addError(key, "不是正确的手机号码");
            }
        }
    }

    /**
     * @param keys
     * @方法名称 tel
     * @功能描述 <pre>电话号码验证</pre>
     * @作者
     */
    private void tel(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isTel(value)) {
                this.addError(key, "不是正确的电话号码");
            }
        }
    }

    /**
     * @param keys
     * @方法名称 phone 联系电话校验
     * @功能描述 <pre>手机/电话号码校验</pre>
     * @作者
     */
    private void phone(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isPhone(value)) {
                this.addError(key, "不是正确的联系电话");
            }
        }
    }

    /**
     * @param keys
     * @方法名称 number 数字,包含小数点
     * @功能描述 <pre></pre>
     * @作者
     */
    private void number(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isNumber(value)) {
                this.addError(key, "不是正确的数字");
            }
        }
    }

    /**
     * @param keys
     * @方法名称 email
     * @功能描述 <pre>校验邮箱</pre>
     * @作者
     */
    private void email(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isEmail(value)) {
                this.addError(key, "不是正确的电子邮箱");
            }
        }
    }

    /**
     * @param keys
     * @方法名称 decimals
     * @功能描述 <pre>校验是否为正整形或带有两位小数点的数字</pre>
     * @作者
     */
    private void decimals(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isDecimals(value)) {
                this.addError(key, "不是正确的数字");
            }
        }
    }

    /**
     * @param keys
     * @方法名称 rightful 合法字符串校验
     * @功能描述 <pre>校验是否包含非法字符串</pre>
     * @作者
     */
    private void rightful(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isRightfulString(value)) {
                this.addError(key, "只能由数字、字母、下划线组成");
            }
        }
    }

    /**
     * @param keys
     * @方法名称 date
     * @功能描述 <pre>日期格式校验</pre>
     * @作者
     */
    private void date(String[] keys) {
        if (null == keys) {
            return;
        }
        String value = null;
        for (String key : keys) {
            value = dataMap.get(key);
            if (StringUtils.isEmpty(value)) {
                continue;
            }
            if (!RegexUtil.isDate(value, null)) {
                this.addError(key, "非法的日期格式");
            }
        }
    }

    /**
     * @param key   错误参数名
     * @param error 错误
     * @方法名称 addError
     * @功能描述 <pre>添加错误信息</pre>
     * @作者
     */
    private void addError(String key, String error) {
        List<String> errors = errorMap.get(key);
        if (null == errors) {
            errors = new ArrayList<String>();
            errorMap.put(key, errors);
        }
        errors.add(error);
    }

    /**
     * @param dataMap
     * @方法名称 setData
     * @功能描述 <pre>设置被校验数据</pre>
     * @作者
     */
    public void setData(Map<String, String> dataMap) {
        this.dataMap = dataMap;
    }

    /**
     * @param ruleMap
     * @方法名称 setRule
     * @功能描述 <pre>设置校验规则</pre>
     * @作者
     */
    public void setRule(Map<String, String[]> ruleMap) {
        this.ruleMap = ruleMap;
    }

    /**
     * @param descriptionMap
     * @方法名称 setDescription
     * @功能描述 <pre>设置错误的描述信息,将会与实际错误进行拼接,如 用户名不能为空 </pre>
     * @作者
     */
    public void setDescription(Map<String, String> descriptionMap) {
        this.descriptionMap = descriptionMap;
    }


    /**
     * @return
     * @方法名称 getError
     * @功能描述 <pre>获取错误</pre>
     * @作者
     */
    public Map<String, List<String>> getError() {
        return errorMap;
    }

    /**
     * @return
     * @方法名称 instance
     * @功能描述 <pre>实例化</pre>
     * @作者
     */
    public static ValidatorUtil instance() {
        return new ValidatorUtil();
    }

    /**
     * 私有化
     */
    private ValidatorUtil() {
        errorMap = new HashMap<>();
    }

    /**
     * 数据集合
     */
    private Map<String, String> dataMap;
    /**
     * 校验规则
     */
    private Map<String, String[]> ruleMap;
    /**
     * 描述
     */
    private Map<String, String> descriptionMap;
    /**
     * 校验错误信息
     */
    private Map<String, List<String>> errorMap;

}
